package com.yeziji.common.context;

import com.yeziji.common.base.IUserDetailsBase;
import com.yeziji.common.base.UserOnlineBase;
import com.yeziji.common.base.UserServletInfoBase;
import com.yeziji.utils.expansion.Opt2;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.Locale;
import java.util.function.BiConsumer;
import java.util.function.Function;

/**
 * 在线工具类
 *
 * @author hwy
 * @since 2023/11/12 14:33
 **/
@Slf4j
public class OnlineContext {
    /**
     * 当前平台
     *
     * <p>每个服务初始化安全框架时对其进行赋值</p>
     */
    private static final ThreadLocal<String> CURRENT_PLATFORM = new ThreadLocal<>();

    /**
     * 定义获取当前线程的变量信息
     */
    private static final ThreadLocal<IUserDetailsBase> CURRENT_USER_ONLINE = new ThreadLocal<>();

    /**
     * 当前用户 servlet 信息
     */
    private static final ThreadLocal<UserServletInfoBase> CURRENT_USER_SERVLET_INFO = new ThreadLocal<>();

    /**
     * 清除线程本地对象
     */
    public static void clear() {
        CURRENT_USER_ONLINE.remove();
        CURRENT_USER_SERVLET_INFO.remove();
    }

    /**
     * 根据 token 存储当前会话对象
     *
     * @param token 令牌信息
     */
    public static void setToken(String token) {
        if (StringUtils.isNotBlank(token)) {
            setCurrentServletInfoParam(token, UserServletInfoBase::setToken);
        }
    }

    /**
     * 获取当前会话令牌
     *
     * @return {@link String} 令牌信息
     */
    public static String getToken() {
        return getCurrentServletInfoParam(UserServletInfoBase::getToken);
    }

    /**
     * 保存当前会话的访问 ip
     *
     * @param ip ip 地址
     */
    public static void setIp(String ip) {
        setCurrentServletInfoParam(ip, UserServletInfoBase::setIp);
    }

    /**
     * 获取当前会话的 IP 地址
     *
     * @return {@link String} IP 地址
     */
    public static String getIp() {
        return getCurrentServletInfoParam(UserServletInfoBase::getIp);
    }

    /**
     * 保存当前会话的调用栈
     *
     * @param mdc 调用栈 id
     */
    public static void setMdc(String mdc) {
        setCurrentServletInfoParam(mdc, UserServletInfoBase::setMdc);
    }

    /**
     * 获取当前会话的调用栈
     *
     * @return {@link String} 调用栈 id
     */
    public static String getMdc() {
        return getCurrentServletInfoParam(UserServletInfoBase::getMdc);
    }

    /**
     * 保存当前会话的请求接口
     *
     * @param path 请求接口
     */
    public static void setPath(String path) {
        setCurrentServletInfoParam(path, UserServletInfoBase::setPath);
    }

    /**
     * 获取当前会话的请求接口
     *
     * @return {@link String} 请求接口
     */
    public static String getPath() {
        return getCurrentServletInfoParam(UserServletInfoBase::getPath);
    }

    /**
     * 申明当前地区语言信息
     *
     * @param locale 地区信息
     */
    public static void setLocale(Locale locale) {
        setCurrentServletInfoParam(Opt2.nullElse(locale, Locale.CHINA), UserServletInfoBase::setLocale);
    }

    /**
     * 获取当前地区语言信息
     *
     * @return {@link Locale}
     */
    public static Locale getLocale() {
        return getCurrentServletInfoParam(UserServletInfoBase::getLocale);
    }

    /**
     * 保存当前平台信息
     *
     * @param platform 平台
     */
    public static void setPlatform(final String platform) {
        CURRENT_PLATFORM.set(Opt2.nullElse(platform, "未知平台"));
    }

    /**
     * 获取当前平台信息
     *
     * @return {@link String} 平台信息
     */
    public static String getPlatform() {
        return CURRENT_PLATFORM.get();
    }

    /**
     * 判断是否存在会话信息
     *
     * @return {@link Boolean} 是否存在
     */
    public static boolean isHasSession() {
        return CURRENT_USER_SERVLET_INFO.get() != null && CURRENT_USER_ONLINE.get() != null;
    }

    /**
     * 赋值当前用户信息
     *
     * @param userDetails 用户信息
     */
    public static void setOnlineUserDetails(IUserDetailsBase userDetails) {
        CURRENT_USER_ONLINE.set(userDetails);
    }

    /**
     * 赋值当前 servlet 信息
     *
     * @param userServletInfoBase 用户信息
     */
    public static void setOnlineUserServletInfo(UserServletInfoBase userServletInfoBase) {
        CURRENT_USER_SERVLET_INFO.set(userServletInfoBase);
    }

    /**
     * 获取当前用户信息
     *
     * @return {@link UserOnlineBase} 用户信息
     */
    public static IUserDetailsBase getOnlineUserOnlineInfo() {
        return Opt2.nullElse(CURRENT_USER_ONLINE.get(), new IUserDetailsBase());
    }

    /**
     * 获取当前 servlet 信息
     *
     * @return {@link UserOnlineBase} 用户信息
     */
    public static UserServletInfoBase getOnlineUserServletInfo() {
        return Opt2.nullElse(CURRENT_USER_SERVLET_INFO.get(), new UserServletInfoBase());
    }

    /**
     * 获取当前的用户 id
     *
     * @return {@link Long} 用户 id
     */
    public static Long getUserId() {
        return getOnlineUserOnlineInfo().getUserId();
    }

    /**
     * 获取当前的用户账号
     *
     * @return {@link String} 用户账号
     */
    public static String getUsername() {
        return getOnlineUserOnlineInfo().getUsername();
    }

    /**
     * 获取当前的指定的数据源
     *
     * @return {@link String} 数据源 key
     */
    public static String getDataSourceKey() {
        return getOnlineUserOnlineInfo().getDataSourceKey();
    }

    /**
     * 是否为管理员
     *
     * @return {@link Boolean} 是否为管理员
     */
    public static boolean isAdmin() {
        return isHasSession() && getOnlineUserOnlineInfo().isAdmin();
    }

    /**
     * 赋值属性变量
     *
     * @param val            赋值
     * @param setterConsumer 属性
     */
    private static <T> void setCurrentServletInfoParam(T val, BiConsumer<UserServletInfoBase, T> setterConsumer) {
        UserServletInfoBase userServletInfoBase = CURRENT_USER_SERVLET_INFO.get();
        if (userServletInfoBase == null) {
            userServletInfoBase = new UserServletInfoBase();
        }
        setterConsumer.accept(userServletInfoBase, val);
        CURRENT_USER_SERVLET_INFO.set(userServletInfoBase);
    }

    /**
     * 獲取屬性變量
     *
     * @param getterFunction 獲取屬性
     * @return {@link String} 屬性值
     */
    private static <T> T getCurrentServletInfoParam(Function<UserServletInfoBase, T> getterFunction) {
        UserServletInfoBase userServletInfoBase = CURRENT_USER_SERVLET_INFO.get();
        if (userServletInfoBase == null) {
            return null;
        }
        return getterFunction.apply(userServletInfoBase);
    }
}
